<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<!-- TOTP Debugger
  --
  -- Copyright 2011 Google Inc.
  -- Author: Markus Gutschke
  --
  -- Licensed under the Apache License, Version 2.0 (the "License");
  -- you may not use this file except in compliance with the License.
  -- You may obtain a copy of the License at
  --
  --      http://www.apache.org/licenses/LICENSE-2.0
  --
  -- Unless required by applicable law or agreed to in writing, software
  -- distributed under the License is distributed on an "AS IS" BASIS,
  -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  -- See the License for the specific language governing permissions and
  -- limitations under the License.
-->
<head>
  <link rel="shortcut icon" href="http://code.google.com/p/google-authenticator/logo" type="image/png">
  <title>TOTP Debugger</title>
  <script src="https://utc-time.appspot.com"></script>
    <!-- Returns a line of the form:
      --   var timeskew = new Date().getTime() - XXX.XX;
      -->

  <script> <!--
// Given a secret key "K" and a timestamp "t" (in 30s units since the
// beginning of the epoch), return a TOTP code.
function totp(K,t) {
  function sha1(C){
    function L(x,b){return x<<b|x>>>32-b;}
    var l=C.length,D=C.concat([1<<31]),V=0x67452301,W=0x88888888,
        Y=271733878,X=Y^W,Z=0xC3D2E1F0;W^=V;
    do D.push(0);while(D.length+1&15);D.push(32*l);
    while (D.length){
      var E=D.splice(0,16),a=V,b=W,c=X,d=Y,e=Z,f,k,i=12;
      function I(x){var t=L(a,5)+f+e+k+E[x];e=d;d=c;c=L(b,30);b=a;a=t;}
      for(;++i<77;)E.push(L(E[i]^E[i-5]^E[i-11]^E[i-13],1));
      k=0x5A827999;for(i=0;i<20;I(i++))f=b&c|~b&d;
      k=0x6ED9EBA1;for(;i<40;I(i++))f=b^c^d;
      k=0x8F1BBCDC;for(;i<60;I(i++))f=b&c|b&d|c&d;
      k=0xCA62C1D6;for(;i<80;I(i++))f=b^c^d;
      V+=a;W+=b;X+=c;Y+=d;Z+=e;}
    return[V,W,X,Y,Z];
  }
  var k=[],l=[],i=0,j=0,c=0;
  for (;i<K.length;){
    c=c*32+'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'.
      indexOf(K.charAt(i++).toUpperCase());
    if((j+=5)>31)k.push(Math.floor(c/(1<<(j-=32)))),c&=31;}
  j&&k.push(c<<(32-j));
  for(i=0;i<16;++i)l.push(0x6A6A6A6A^(k[i]=k[i]^0x5C5C5C5C));
  var s=sha1(k.concat(sha1(l.concat([0,t])))),o=s[4]&0xF;
  return ((s[o>>2]<<8*(o&3)|(o&3?s[(o>>2)+1]>>>8*(4-o&3):0))&-1>>>1)%1000000;
}

// Periodically check whether we need to update the UI. It would be a little
// more efficient to only call this function as a direct result of
// significant state changes. But polling is cheap, and keeps the code a
// little easier.
var lastsecret,lastlabel,lastepochseconds,lastoverrideepoch;
var lasttimestamp,lastoverride,lastsearch;
function refresh() {
  // Compute current TOTP code
  var k=document.getElementById('secret').value.
    replace(/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ234567]/gi, '');
  var d=document.getElementById('overrideepoch').value.replace(/[^0-9]/g, '');
  if (d) e=parseInt(d); else e=Math.floor(new Date().getTime()/1000);
  var t=Math.floor(e/30);
  var s=document.getElementById('override').value.replace(/[^0-9]/g, '');
  if (s) { t=parseInt(s); e=30*t; }
  var label=escape(document.getElementById('label').value);
  var search=document.getElementById('search').value;

  // If TOTP code has changed (either because of user edits, or
  // because of elapsed time), update the user interface.
  if (k != lastsecret || label != lastlabel || e != lastepochseconds ||
      d != lastoverrideepoch || t != lasttimestamp || s != lastoverride ||
      search != lastsearch) {
    if (d != lastoverrideepoch) {
      document.getElementById('override').value = '';
      s = '';
    } else if (s != lastoverride) {
      document.getElementById('overrideepoch').value = '';
      d = '';
    }
    lastsecret=k;
    lastlabel=label;
    lastepochseconds=e;
    lastoverrideepoch=d;
    lasttimestamp=t;
    lastoverride=s;
    lastsearch=search;

    var code=totp(k,t);

    // Compute the OTPAuth URL and the associated QR code
    var h='https://www.google.com/chart?chs=200x200&chld=M|0&'+
      'cht=qr&chl=otpauth://totp/'+encodeURI(label)+'%3Fsecret%3D'+k;
    var a=document.getElementById('authurl')
    a.innerHTML='otpauth://totp/'+label+'?secret='+k;
    a.href=h;
    document.getElementById('aqr').href=h;
    var q=document.getElementById('qr');
    q.src=h;
    q.alt=label+' '+k;
    q.title=label+' '+k;

    // Show the current time in seconds and in 30s increments since midnight
    // Jan 1st, 1970. Optionally, let the user override this timestamp.
    document.getElementById('epoch').innerHTML=e;
    document.getElementById('ts').innerHTML=t;

    // Show the current TOTP code.
    document.getElementById('totp').innerHTML=code;

    // If the user manually entered a TOTP code, try to find a matching code
    // within a 25h window.
    var result='';
    if (search && !!(search=parseInt(search))) {
      for (var i=0; i < 25*120; ++i) {
        if (search == totp(k, t+(i&1?-Math.floor(i/2):Math.floor(i/2)))) {
          if (i<2) {
            result='&nbsp;';
            break;
          }
          if (i >= 120) {
            result=result + Math.floor(i/120) + 'h ';
            i%=120;
          }
          if (i >= 4) {
            result=result + Math.floor(i/4) + 'min ';
            i%=4;
          }
          if (i&2) {
            result=result + '30s ';
          }
          if (i&1) {
            result='Code was valid ' + result + 'ago';
          } else {
            result='Code will be valid in ' + result;
          }
          break;
        }
      }
      if (!result) {
        result='No such code within a &#177;12h window';
      }
    }
    document.getElementById('searchresult').innerHTML=result + '&nbsp;';

    // If possible, compare the current time as reported by Javascript
    // to "official" time as reported by AppEngine. If there is any significant
    // difference, show a warning message. We always expect at least a minor
    // time skew due to round trip delays, which we are not bothering to
    // compensate for.
    if (typeof timeskew != undefined) {
      var ts=document.getElementById('timeskew');
      if (Math.abs(timeskew) < 2000) {
        ts.style.color='';
        ts.innerHTML="Your computer's time is set correctly. TOTP codes " +
                     "will be computed accurately.";
      } else if (Math.abs(timeskew) < 30000) {
        ts.style.color='';
        ts.innerHTML="Your computer's time is off by " +
          (Math.round(Math.abs(timeskew)/1000)) + " seconds. This is within " +
          "acceptable tolerances. Computed TOTP codes might be different " +
          "from the ones in the mobile application, but they will be " +
          "accepted by the server.";
      } else {
        ts.style.color='#dd0000';
        ts.innerHTML="<b>Your computer's time is off by " +
          (Math.round(Math.abs(timeskew)/1000)) + " seconds. Computed TOTP " +
          "codes are probably incorrect.</b>";
      }
    }
  }
}
  --></script>
</head>
<body style="font-family: sans-serif" onload="setInterval(refresh, 100)">
  <h1>TOTP Debugger</h1>
  <table>
    <tr><td colspan="7"><a style="text-decoration: none; color: black"
                           id="authurl"></a></td></tr>
    <tr><td colspan="3">Enter&nbsp;secret&nbsp;key&nbsp;in&nbsp;BASE32:&nbsp;</td>
        <td><input type="text" id="secret" /></td>
        <td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
        <td rowspan="8"><a style="text-decoration: none" id="aqr">
          <img id="qr" border="0"></a></td><td width="100%">&nbsp;</td></tr>
    <tr><td colspan="3">Account&nbsp;label:&nbsp;</td>
        <td><input type="text" id="label" /></td></tr>
    <tr><td>Interval:&nbsp;</td><td align="right">30s</td><td>&nbsp;</td></tr>
    <tr><td>Time:&nbsp;</td><td align="right">
          <span id="epoch"></span></td><td>&nbsp;</td><td>
          <input type="text" id="overrideepoch" /></td></tr>
    <tr><td>Timestamp:&nbsp;</td><td align="right"><span id="ts"></span></td>
        <td>&nbsp;</td><td><input type="text" id="override" /></td></tr>
    <tr><td>TOTP:&nbsp;</td><td align="right"><span id="totp"></span></td>
        <td>&nbsp;</td><td><input type="text" id="search" /></td></tr>
    <tr><td colspan="4" id="searchresult"></td></tr>
  </table>
  <br />
  <div id="timeskew" style="width: 80%"></div>
  <br />
  <br />
  <div style="width: 80%; color: #dd0000; font-size: small">
    <p><b>WARNING!</b> This website is a development and debugging tool only.
    Do <b>not</b> use it to generate security tokens for logging into your
    account. You should never store, process or otherwise access the secret
    key on the same machine that you use for accessing your account. Doing so
    completely defeats the security of two-step verification.</p>
    <p>Instead, use one of the mobile phone clients made available by the
    <a href="http://code.google.com/p/google-authenticator">Google
    Authenticator</a> project. Or follow the <a href="http://www.google.com/support/accounts/bin/static.py?hl=en&page=guide.cs&guide=1056283&topic=1056285">instructions</a>
    provided by Google.</p>
    <p>If you ever entered your real secret key into this website, please
    immediately <a href="https://www.google.com/accounts/SmsAuthConfig">reset
    your secret key</a>.</p>
  </div>
</body>
</html>
